/*
 * Copyright:  2018 smarabbit studio.
 *
 *  Licensed under the Confluent Community License; you may not use this file
 *  except in compliance with the License.  You may obtain a copy of the License at
 *
 *  http://www.confluent.io/confluent-community-license
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OF ANY KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations under the License.
 *
 *  @作   者： 黄开晖 (117227773@qq.com)
 *  @日   期:  2019年1月7日
 */

package com.massyframework.beanskin.springboot.beancontext.provider;

import java.util.Optional;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import org.springframework.web.context.ConfigurableWebApplicationContext;
import org.springframework.web.context.ContextLoaderListener;

import com.massyframework.beanskin.assembly.Assembly;
import com.massyframework.beanskin.assembly.AssemblyContextDelegate;
import com.massyframework.beanskin.assembly.handling.ActivationHandler;
import com.massyframework.beanskin.assembly.handling.EnvironmentHandler;
import com.massyframework.beanskin.assembly.handling.HandlerRegistration;
import com.massyframework.beanskin.assembly.handling.ReadyingException;
import com.massyframework.beanskin.assembly.handling.ReadyingHandler;
import com.massyframework.beanskin.service.ServiceRegistration;
import com.massyframework.beanskin.service.ServiceRegistry;
import com.massyframework.beanskin.service.ServiceRepository;
import com.massyframework.beanskin.springboot.beancontext.DependencyServiceBeanDefinitionRegistryPostProcessor;
import com.massyframework.beanskin.springboot.beancontext.DependencyServiceInitializer;
import com.massyframework.beanskin.springboot.beancontext.SpringBeanContextAdapter;
import com.massyframework.beanskin.springboot.beancontext.WebApplicationContextFactory;


final class SpringListenerHandler extends DependencyServiceBeanDefinitionRegistryPostProcessor
        implements EnvironmentHandler, ReadyingHandler, ActivationHandler {


    private volatile ServiceRegistration<ServletContextListener> registration;

    private volatile HandlerRegistration<AssemblyContextDelegate<SpringBeanContextAdapter>> contextRegistration;
    private volatile ContextLoaderListener contextLoaderListener;
    private volatile ServletContextEvent event;
    private volatile boolean runAtJ2EE = false;


    /**
     * 构造方法
     */
    public SpringListenerHandler() {

    }

    /**
     * 向Web环境注册ServletContextListener，Filter, Servlet等组件。
     * 仅当运行在j2ee环境下才调用本方法。
     *
     * @throws Exception {@link Exception},初始化发生的非预期例外
     */
    @Override
    public void init() throws Exception {
        final Assembly assembly = this.getAssociatedAssembly();
        ServiceRepository repo = this.getServiceRepository(assembly);

        Optional<ServletContext> optional = repo.findService(ServletContext.class);
        this.runAtJ2EE = optional.isPresent();
    }

    /**
     * 执行就绪
     *
     * @throws ReadyingException 就绪时发生例外
     */
    @Override
    public void readying() throws ReadyingException {
        if (!this.runAtJ2EE) return;
        this.exportService();
    }

    /**
     * 输出服务，服务输出后，DelegateListener才能和输出的服务挂接上
     */
    protected synchronized void exportService() {
        if (this.registration == null) {
            //注册服务
        	Assembly assembly = this.getAssociatedAssembly();
            ServiceRepository repository = this.getServiceRepository(assembly);
            ServiceRegistry registry = repository.getServiceRegistry();

            Bridge bridge = new Bridge();

            this.registration =
                    registry.register(ServletContextListener.class, bridge, null);
        }

    }

    /**
     * 退出就绪
     *
     * @throws ReadyingException 退出就绪时发生的例外
     */
    @Override
    public void unreadyed() throws ReadyingException {
        // 依赖服务未就绪
        this.unexportService();
    }

    /**
     * 撤销服务输出
     */
    protected synchronized void unexportService(){
        //注销
        if (this.registration != null){
            this.registration.unregister();
            this.registration = null;
        }
    }

    /**
     * 在Web场景下注册的组件是否已准备就绪？<br>
     * Servlet的实例化要视注册时的选项，如果是首次请求时才实例化，那在发起首次请求前，本方法只能返回<code>false</code>
     *
     * @return {@link boolean},返回<code>true</code>表示已就绪，否则返回<code>false</code>
     */
    @Override
    public boolean isReady() {
        if (!this.runAtJ2EE) return false;
        return this.event != null;
    }

    /**
     * 启动,使得进入激活状态
     *
     * @throws Exception 启动时发生非预期例外
     */
    @Override
    public synchronized void doStarting() throws Exception {
        if (this.contextRegistration == null) {
        	Assembly assembly = this.getAssociatedAssembly();

            try {
                //创建ContextLoaderListener             
                WebApplicationContextFactory factory =
                        new WebApplicationContextFactory();
                DependencyServiceInitializer<ConfigurableWebApplicationContext> depenedencyInitializer =
                		new DependencyServiceInitializer<>(this);

                ConfigurableWebApplicationContext context =
                        factory.createApplicationContext(assembly,
                        		depenedencyInitializer);
                SpringBeanContextAdapter adapter =
                        new SpringBeanContextAdapter(context);
                AssemblyContextDelegate<SpringBeanContextAdapter> delegate =
                		new AssemblyContextDelegate<SpringBeanContextAdapter>(adapter);
                delegate.init(this.getLifecycleManager());
                this.setAssemblyContext(delegate);

                /*
                this.contextLoaderListener = new ContextLoaderListenerEx(this);
                */
                this.contextLoaderListener = new ContextLoaderListener(context);
                contextLoaderListener.contextInitialized(this.event);
                
                this.getLifecycleManager().register(delegate);
            }catch(Exception e) {
                throw e;
            }
        }
    }

    /**
     * 已停止，退出激活状态
     *
     * @throws Exception 停止时发生非预期例外
     */
    @Override
    public synchronized void doStopped() throws Exception {
        if (this.contextRegistration != null) {
            AssemblyContextDelegate<SpringBeanContextAdapter> context =
                    this.contextRegistration.getHandler();
            this.contextRegistration.unregister();
            this.contextRegistration = null;

            this.contextLoaderListener.contextDestroyed(this.event);
            
            try {
            	context.getBeanContext().getApplicationContext().close();
            }finally {
            	this.setAssemblyContext(null);
            	this.contextLoaderListener= null;
            }
        }
    }

    /**
     * 初始化ServletContext
     * @param sce {@link ServletContextEvent}
     */
    protected void initServletContext(ServletContextEvent sce) {
        this.event = sce;
        try {
            this.getLifecycleManager().start();
        }catch(Exception e) {
            this.logError(e.getMessage(), e);
        }
    }

    /**
     * 释放ServletContext
     * @param sce {@link ServletContextEvent}
     */
    protected void destortedServletContext(ServletContextEvent sce) {
        this.event = null;
    }

    /**
     * 服务仓储
     * @param {@link Assembly},装配件
     * @return {@link ServiceRepository}, 服务仓储
     */
    protected ServiceRepository getServiceRepository(Assembly assembly){
        ServiceRepository repo = ServiceRepository.retrieveFrom(assembly);
        return repo;
    }

    private class Bridge implements ServletContextListener {

        @Override
        public void contextInitialized(ServletContextEvent sce) {
            initServletContext(sce);
        }

        @Override
        public void contextDestroyed(ServletContextEvent sce) {
            destortedServletContext(sce);
        }

    }


}
